Output recurrent & Hidden layer recurrent

은닉 순환 & 출력 순환
RNN은 기본적으로 은닉층에 순환 성질을 가진 모델이지만, 출력층에서 오는 순환 연결을 가진 모델도 있다.
출력층의 o_t-1을 추가하는 방법은 아래의 두가지가 있다.

- 현재 타임 스텝에서 은닉충 h_t에 추가
- 현재 타임 스텝에서 출력층 o_t에 추가
RNN 정방향 계산
tf.kears API의 SimpleRNN 클래스로 출력-출력 순환과 비슷한 순환 층을 정의할 수 있다.
(순환 신경망에서 활성화 함수를 거친 값을 은닉 상태라고 부르기 때문에 SimpleRNN을 은닉-은닉 순환으로 볼 수 있다.
import tensorflow as tf
tf.random.set_seed(1)
run_layer=tf.keras.layers.SimpleRNN(units=2, use_bias=True, return_sequences=True)
run_layer.build(input_shape=(None, None, 5))
w_xh, w_oo, b_h=run_layer.weights
print('W_xh :', w_xh.shape)
print('W_oo :', w_oo.shape)
print('b_h :', b_h.shape)

W_xh 크기: (5, 2)

W_oo 크기: (2, 2)

b_h 크기: (2,)

input_shape=(None, None, 5)
에서 첫 번째 매개변수는 Batch size,  두 번째 매개변수는 Sequences size, 세 번째 매개변수는 특성 크기이다.
run_layer 계산 과정
x_seq=tf.convert_to_tensor([[1.0]*5, [2.0]*5, [3.0]*5], dtype=tf.float32)
output=run_layer(tf.reshape(x_seq, shape=(1, 3, 5)))
수동 계산
out_man=[]
for t in range(len(x_seq)):
xt=tf.reshape(x_seq[t], (1, 5))
print(' {} =>'.format(t))
print(' :',xt.numpy())
ht=tf.matmul(xt, w_xh)+b_h
print(' :',ht.numpy())
if t>0:
prev_o=out_man[t-1]
else:
prev_o=tf.zeros(shape=(ht.shape))
ot=ht+tf.matmul(prev_o, w_oo)
ot=tf.math.tanh(ot)
out_man.append(ot)
print(' () :', ot.numpy())
print(' SimpleRNN :'.format(t), output[0][t].numpy())
print()

타입 스텝 0 =>

 입력          : [[1. 1. 1. 1. 1.]]

 은닉          : [[0.41464037 0.96012145]]

 출력(수동)    : [[0.39240566 0.74433106]]

 SimpleRNN출력 : [0.39240566 0.74433106]


타입 스텝 1 =>

 입력          : [[2. 2. 2. 2. 2.]]

 은닉          : [[0.82928073 1.9202429 ]]

 출력(수동)    : [[0.8011651 0.9912947]]

 SimpleRNN출력 : [0.8011651 0.9912947]


타입 스텝 2 =>

 입력          : [[3. 3. 3. 3. 3.]]

 은닉          : [[1.243921  2.8803642]]

 출력(수동)    : [[0.9546827 0.999307 ]]

 SimpleRNN출력 : [0.9546827 0.999307 ]

SimpleRNN에서 phi_h는 선형활성화 함수를 phi_o는 탄제트(tanh) 활성화 함수를 사용한다.(default value)